﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class FM_Helper
{
    public static string TAG = Constants.TAG;
    static string do_a = "";
    static string re_a = "";
    static string mi_a = "";
    static string fa_a = "";
    static string sol_a = "";
    static string la_a = "";
    static string si_a = "";

    static void StartMeasure()
    {
        do_a = "c";
        re_a = "d";
        mi_a = "e";
        fa_a = "f";
        sol_a = "g";
        la_a = "a";
        si_a = "b";
    }

    static string ApplyTonality(FM_KeySignatureValue keySignature, string key)
    {
        //send it only the note, without the octave!!
        if (key.Contains("n") ||
                key.Contains("#") ||
                key.Contains("##") ||
                key.Contains("###") ||
                (key.EndsWith("b") && key.Length == 2) ||
                (key.EndsWith("bb") && key.Length == 3) ||
                (key.EndsWith("bbb") && key.Length == 4))
            return key;

        if (keySignature == FM_KeySignatureValue.SOL || keySignature == FM_KeySignatureValue.MIm)
        {
            if (key == "f") return key + "#";
        }
        if (keySignature == FM_KeySignatureValue.RE || keySignature == FM_KeySignatureValue.SIm)
        {
            if (key == "f" || key == "c") return key + "#";
        }
        if (keySignature == FM_KeySignatureValue.LA || keySignature == FM_KeySignatureValue.FAsharpm)
        {
            if (key == "f" || key == "c" || key == "g") return key + "#";
        }
        if (keySignature == FM_KeySignatureValue.MI || keySignature == FM_KeySignatureValue.DOsharpm)
        {
            if (key == "f" || key == "c" || key == "g" || key == "d")
                return key + "#";
        }
        if (keySignature == FM_KeySignatureValue.SI || keySignature == FM_KeySignatureValue.SOLsharpm)
        {
            if (key == "f" || key == "c" || key == "g" || key == "d" || key == "a")
                return key + "#";
        }
        if (keySignature == FM_KeySignatureValue.FAsharp || keySignature == FM_KeySignatureValue.REsharpm)
        {
            if (key == "f" || key == "c" || key == "g" || key == "d" || key == "a" || key == "e")
                return key + "#";
        }
        if (keySignature == FM_KeySignatureValue.DOsharp || keySignature == FM_KeySignatureValue.LAsharpm)
        {
            if (key == "f" || key == "c" || key == "g" || key == "d" || key == "a" || key == "e" || key == "b")
                return key + "#";
        }
        if (keySignature == FM_KeySignatureValue.FA || keySignature == FM_KeySignatureValue.REm)
        {
            if (key == "b") return key + "b";
        }
        if (keySignature == FM_KeySignatureValue.SIb || keySignature == FM_KeySignatureValue.SOLm)
        {
            if (key == "b" || key == "e") return key + "b";
        }
        if (keySignature == FM_KeySignatureValue.MIb || keySignature == FM_KeySignatureValue.DOm)
        {
            if (key == "b" || key == "e" || key == "a") return key + "b";
        }
        if (keySignature == FM_KeySignatureValue.LAb || keySignature == FM_KeySignatureValue.FAm)
        {
            if (key == "b" || key == "e" || key == "a" || key == "d")
                return key + "b";
        }
        if (keySignature == FM_KeySignatureValue.REb || keySignature == FM_KeySignatureValue.SIbm)
        {
            if (key == "b" || key == "e" || key == "a" || key == "d" || key == "g")
                return key + "b";
        }
        if (keySignature == FM_KeySignatureValue.SOLb || keySignature == FM_KeySignatureValue.MIbm)
        {
            if (key == "b" || key == "e" || key == "a" || key == "d" || key == "g" || key == "c")
                return key + "b";
        }
        if (keySignature == FM_KeySignatureValue.DOb || keySignature == FM_KeySignatureValue.LAbm)
        {
            if (key == "b" || key == "e" || key == "a" || key == "d" || key == "g" || key == "c" || key == "f")
                return key + "b";
        }
        return key;
    }

    public static string NoteToString(FM_Note note, FM_KeySignatureValue keySignature)
    {
        string result = "";
        var value = note.GetNote();
        if (value == FM_NoteValue.DO) result = "c";
        if (value == FM_NoteValue.RE) result = "d";
        if (value == FM_NoteValue.MI) result = "e";
        if (value == FM_NoteValue.FA) result = "f";
        if (value == FM_NoteValue.SOL) result = "g";
        if (value == FM_NoteValue.LA) result = "a";
        if (value == FM_NoteValue.SI) result = "b";
        int accidental = note.GetAccidental();
        if (accidental > 100) accidental = /*(FM_Accidental)*/(accidental - 100);
        if (accidental == FM_Accidental.Natural) result = result + "n";
        if (accidental == FM_Accidental.Flat) result = result + "b";
        if (accidental == FM_Accidental.Sharp) result = result + "#";
        if (accidental == FM_Accidental.DoubleSharp) result = result + "##";
        if (accidental == FM_Accidental.DoubleFlat) result = result + "bb";
        if (accidental == FM_Accidental.TripleSharp) result = result + "###";
        if (accidental == FM_Accidental.TripleFlat) result = result + "bbb";
        result = ComputeNote(keySignature, result);
        result = result + "/" + note.GetOctave();
        return result;
    }

    static string ComputeNote(FM_KeySignatureValue keySignature, string note)
    {
        string initial_note = note;
        string tmp = ApplyTonality(keySignature, note);
        if (note.StartsWith("c"))
        {
            if (!note.Equals("c")) do_a = note;
            else note = do_a;
        }
        if (note.StartsWith("d"))
        {
            if (!note.Equals("d")) re_a = note;
            else note = re_a;
        }
        if (note.StartsWith("e"))
        {
            if (!note.Equals("e")) mi_a = note;
            else note = mi_a;
        }
        if (note.StartsWith("f"))
        {
            if (!note.Equals("f")) fa_a = note;
            else note = fa_a;
        }
        if (note.StartsWith("g"))
        {
            if (!note.Equals("g")) sol_a = note;
            else note = sol_a;
        }
        if (note.StartsWith("a"))
        {
            if (!note.Equals("a")) la_a = note;
            else note = la_a;
        }
        if (note.StartsWith("b"))
        {
            if (!note.Equals("b")) si_a = note;
            else note = si_a;
        }
        if (initial_note.Equals(note)) note = tmp;
        return note;
    }

    public class TestKeyNotes
    {
        public int key;
        public FM_Note note;
        public TestKeyNotes(int key, FM_Note note)
        {
            this.key = key;
            this.note = note;
        }
    }

    public static readonly List<TestKeyNotes> testMap = new List<TestKeyNotes>();

    /*static*/
    FM_Audio_Song TempSongToSong(FM_Temp_Audio_Song input, FM_ScoreBase score, int tempo)
    {
        FM_Audio_Song result = new FM_Audio_Song();
        result.KeySignature = input.KeySignature;
        FM_Audio_Measure m = new FM_Audio_Measure();
        result.Measures.Add(m);
        int measure = 0;
        Dictionary<int, int> voice_duration = new Dictionary<int, int>();
        StartMeasure();
        FM_Audio_Note oldNote = null;

        //go through the chords
        for (int i = 0; i < input.Chords.Count; i++)
        {
            //if starting a new measure, increase the measure index and clear the durations
            if (measure != input.Chords[i].Measure)
            {
                m = new FM_Audio_Measure();
                result.Measures.Add(m);
                measure += 1;
                StartMeasure();
                voice_duration.Clear();
            }

            //from the durations subtract the smallest
            int min;
            try
            {
                min = voice_duration.Values.AsArray().Min();
            }
            catch (System.Exception e)
            {
                min = 0;
                Debug.LogError(e.Message);
            }
            foreach (int vd in voice_duration.Keys.AsList())
            {
                voice_duration.TryGetValue(vd, out int value);
                voice_duration[vd] = value - min;
            }

            FM_Audio_Note n = new FM_Audio_Note();
            List<int> tracksInTie = new List<int>();              //tracks if we are in a legato
            List<int> durationsInTie = new List<int>();           //duration of each track if we are in a legato
            List<int> tracksOutsideTie = new List<int>();         //tracks if we are outside a legato
            List<int> durationsOutsideTie = new List<int>();      //duration of each track if we are outside a legato
                                                                  //clear everything
            n.Legato = false;
            n.AudioIntInLegato = -1;
            n.AudioIntOutsideLegato = -1;
            n.AudioTrackInLegato = null;
            n.AudioTrackOutsideLegato = null;
            bool is_pause = true;
            bool is_chord = false;
            //begin processing, going through all the notes in the current chord
            for (int nIndex = 0; nIndex < input.Chords[i].Notes.Count; nIndex++)
            {
                FM_BaseNote note = input.Chords[i].Notes[nIndex];
                int d = FM_SoundPool.GetDurationInMs(note.GetDuration(), note.tupletSize, tempo, input.TimeSignature_d);    //the duration

                voice_duration.TryGetValue(note.voice, out int value);
                voice_duration[note.voice] = value + d;                   //add the duration to the duration list
                                                                                                                    //if the current note is a PAUSE
                if (note.GetNoteType() == FM_NoteType.PAUSE)
                {
                    durationsOutsideTie.Add(d);
                    durationsInTie.Add(d);
                    tracksInTie.Add(-1);
                    tracksOutsideTie.Add(-1);
                    if (oldNote != null) oldNote.NextPause = true;
                }
                //if the current note is a NOTE
                if (note.GetNoteType() == FM_NoteType.NOTE)
                {
                    FM_Note fmNote = (FM_Note)note;
                    int track = FM_SoundPool.GetIndex(NoteToString(fmNote, input.KeySignature));
                    // Adjust logging based on the C# environment
                    Debug.Log(i + " TempSongToSong add key:" + track + " note:" + fmNote.note + " octave:" + fmNote.octave + " accidental:" + fmNote.accidental + " clef:" + fmNote.clef);
                    // Adjust testMap usage based on the C# environment
                    // testMap.Add(new TestKeyNotes(track, fmNote));

                    // if the note is not the beginning nor the end of a tie
                    if (!((FM_Note)note).isTieStart && !((FM_Note)note).isTieEnd)
                    {
                        tracksOutsideTie.Add(track);
                        durationsOutsideTie.Add(d);
                        tracksInTie.Add(track);
                        durationsInTie.Add(d);
                        is_pause = false;
                    }
                    //if the note is the end of a tie
                    else if (!((FM_Note)note).isTieStart)
                    {
                        tracksOutsideTie.Add(track);
                        durationsOutsideTie.Add(d);
                        tracksInTie.Add(-1);
                        durationsInTie.Add(0);
                        is_pause = false;
                    }
                    //if the note is the beginning of a tie
                    else
                    {
                        n.Legato = true;
                        tracksOutsideTie.Add(track);
                        durationsOutsideTie.Add(d);
                        tracksInTie.Add(track);
                        //find the duration of the note that is the end of this particular tie
                        int lDuration = 0;
                        for (int t = 0; t < score.Ties.Count; t++)
                            if (score.Ties[t].s == note)
                                lDuration = FM_SoundPool.GetDurationInMs(score.Ties[t].e.GetDuration(), score.Ties[t].e.tupletSize, tempo, input.TimeSignature_d);
                        durationsInTie.Add((int)d + lDuration);
                        is_pause = false;
                        is_chord = true;
                    }
                }
            }
            n.PauseDuration = voice_duration.Values.Min();
            n.PlayDurationInTie = durationsInTie.Max();
            n.PlayDurationOutsideTie = durationsOutsideTie.Max();
            if (!is_pause)
            {
                int track_count = 0;
                int track = -1;
                foreach (int t in tracksInTie)         //cate track-uri non-pauza sunt?
                    if (t != -1)
                    {
                        track_count += 1;
                        track = t;
                    }
                if (track_count == 1 && !is_chord)
                {
                    n.AudioIntInLegato = track;
                    n.AudioTrackInLegato = null;
                }
                else
                {
                    n.AudioIntInLegato = 0;
                    n.AudioTrackInLegato = FM_SoundPool.GetInstance().CreateTrack(tracksInTie, durationsInTie);
                }

                track_count = 0;
                track = -1;
                foreach (int t in tracksOutsideTie)         //cate track-uri non-pauza sunt?
                    if (t != -1)
                    {
                        track_count += 1;
                        track = t;
                    }
                if (track_count == 1 && !is_chord)
                {
                    n.AudioIntOutsideLegato = track;
                    n.AudioTrackOutsideLegato = null;
                }
                else
                {
                    n.AudioIntOutsideLegato = 0;
                    n.AudioTrackOutsideLegato = FM_SoundPool.GetInstance().CreateTrack(tracksOutsideTie, durationsOutsideTie);
                }
            }
            m.notes.Add(n);
            oldNote = n;
        }
        return result;
    }

    public FM_Audio_Song GenerateSongFromScore(FM_ScoreBase obj, int tempo)
    {
        FM_Temp_Audio_Song song = new FM_Temp_Audio_Song();
        song.KeySignature = obj.GetKeySignature();
        song.TimeSignature_d = obj.GetTimeSignature_d();
        int measure = 0;
        for (int i = 0; i < obj.GetNoteCount(); i++)
        {
            FM_BaseNote note = obj.GetNote(i);
            if (note.GetNoteType() == FM_NoteType.BAR) measure += 1;
            else
            {
                if (obj.GetNote(i).GetNoteType() == FM_NoteType.NOTE || obj.GetNote(i).GetNoteType() == FM_NoteType.PAUSE || obj.GetNote(i).GetNoteType() == FM_NoteType.CHORD)
                {
                    FM_Temp_Audio_Note temp_chord = new FM_Temp_Audio_Note();
                    temp_chord.Measure = measure;
                    if (note.GetNoteType() == FM_NoteType.NOTE) temp_chord.Notes.Add(note);
                    if (note.GetNoteType() == FM_NoteType.CHORD)
                    {
                        FM_Chord chord = (FM_Chord)note;
                        for (int j = 0; j < chord.Notes.Count; j++)
                        {
                            if (chord.Notes[j].GetNoteType() == FM_NoteType.NOTE) temp_chord.Notes.Add(chord.Notes[j]);
                            if (chord.Notes[j].GetNoteType() == FM_NoteType.PAUSE) temp_chord.Notes.Add(chord.Notes[j]);
                        }
                    }
                    if (note.GetNoteType() == FM_NoteType.PAUSE) temp_chord.Notes.Add(note);
                    song.Chords.Add(temp_chord);
                }
            }
        }
        return TempSongToSong(song, obj, tempo);
    }

}